package org.knot.autojmx;

import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.management.ObjectInstance;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.knot.autojmx.annotations.AutoJMX;
import org.knot.autojmx.annotations.JMXMethod;
import org.knot.autojmx.annotations.JMXProperty;
import org.knot.autojmx.core.DynamicMBeanImpl;
import org.knot.autojmx.enums.Rights;
import org.knot.autojmx.metadata.AutoJMXFieldMap;
import org.knot.autojmx.metadata.MethodField;
import org.knot.autojmx.metadata.NotificationField;
import org.knot.autojmx.metadata.PropertyField;


/**
 * AutoJMX处理类
 * 
 * @author <a href="mailto:yuxiaoweimail@gmail.com">yuxiaowei</a>
 *
 */
public class AnnotationAutoJMXHandler implements AutoJMXHandler {
	
	protected final Log logger = LogFactory.getLog(getClass());
	
	private ObjectInstance instance = null;
	
	private String instanceName  = "";

	private AutoJMXFieldMap m_jmxBeanFieldMap;

	private Object bean;

	private DynamicMBeanImpl m_MBean;

	private ObjectName m_objectName;

	/**
	 * flag MBean is registered status .
	 */
	private boolean m_registered;
	 
	private String m_completeObjNameElt = "";
 
	private String m_objNameWODomainElt;

	private String m_domainElt;

	
	public AnnotationAutoJMXHandler(Object bean) {
		this.m_jmxBeanFieldMap = new AutoJMXFieldMap();
		this.bean = bean;
		this.instanceName = bean.getClass().getSimpleName();
		logger.info("Auto JMX Handler construct scuess.");
	}
	
	public AnnotationAutoJMXHandler(Object bean, String intance) {
		this.m_jmxBeanFieldMap = new AutoJMXFieldMap();
		this.bean = bean;
		this.instanceName = intance;
		logger.info("Auto JMX Handler construct scuess.");
	}

	 
	/* (non-Javadoc)
	 * @see com.dhgate.autojmx.AutoJMXHandler#init()
	 */
	public void init() {
		AutoJMX jmxBean = bean.getClass().getAnnotation(AutoJMX.class);
		if (null != jmxBean) {
			m_completeObjNameElt = jmxBean.objectname();
			m_domainElt = jmxBean.domain();
			m_objNameWODomainElt = jmxBean.name();
			m_jmxBeanFieldMap.setDescription(jmxBean.description());
			Field[] fields = bean.getClass().getDeclaredFields();
			if (null != fields && fields.length > 0)
			{
				for (Field field : fields) {
					JMXProperty jmxproperty = field.getAnnotation(JMXProperty.class);
					PropertyField propertyField = null;
					String name = "";
					boolean notif = false;
					if (null != jmxproperty) {
						name = jmxproperty.name();
						if ("".equals(name)) {
							name = field.getName();
						}
						String right = jmxproperty.rights().getValue();
						notif = jmxproperty.notification();
						propertyField = new PropertyField(name, field, right, field
								.getType().getSimpleName());
					} else {
						name = field.getName();
						propertyField = new PropertyField(name, field, Rights.READ
								.getValue(), field.getType().getSimpleName());
					}
					propertyField.setNotifiable(notif);
					if (notif) {
						NotificationField notification = new NotificationField(name,
								field, null);
						m_jmxBeanFieldMap.addNotificationFromName(name, notification);
					}
					m_jmxBeanFieldMap.addPropertyFromName(name, propertyField);
		
					logger.info("property exposed: name: " + propertyField.getName() + "  field: " + propertyField.getField().getName() + " right: " +  propertyField.getRights() + ", Notif=" + notif);
				}
			}
			Method[] methods = bean.getClass().getDeclaredMethods();
			for (Method method : methods) {
				JMXMethod jmxMethod = method.getAnnotation(JMXMethod.class);
				MethodField methodField = null;
				String name = method.getName();
				if (null != jmxMethod) {
					methodField = new MethodField(method, jmxMethod.description());
				} else {
					methodField = new MethodField(method, "");
				}
				m_jmxBeanFieldMap.addMethodFromName(name, methodField);
				logger.info("method exposed: name: " + methodField.getName() + "  return type: " + methodField.getReturnType());
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.dhgate.autojmx.AutoJMXHandler#start()
	 */
	public void start() {
		m_MBean = new DynamicMBeanImpl(m_jmxBeanFieldMap, bean);
		try {
			m_objectName = new ObjectName(getObjectNameString());
			instance = ManagementFactory
					.getPlatformMBeanServer().registerMBean(m_MBean,
							m_objectName);
			m_registered = true;
		} catch (Exception e) {
			 logger.error("Registration of MBean failed.", e);
		}

	}

	/**
	 * Returns the object name of the exposed jxmBean.
	 * 
	 * @return the object name of the exposed jxmBean.
	 */
	private String getObjectNameString() {
		if (m_completeObjNameElt != null && !"".equals(m_completeObjNameElt)) {
			return m_completeObjNameElt;
		}
		String domain;
		if (m_domainElt != null && !"".equals(m_domainElt)) {
			domain = m_domainElt;
		} else {
			domain = getPackageName(bean.getClass().getName());
		}
		String name = "type=" + bean.getClass().getName() + ",instance="
				+ instanceName;
		if (m_objNameWODomainElt != null  && !"".equals(m_objNameWODomainElt)) {
			name = "name=" + m_objNameWODomainElt;
		}

		StringBuffer sb = new StringBuffer();
		if ((domain != null) && (domain.length() > 0)) {
			sb.append(domain + ":");
		}
		sb.append(name);
		logger.info("jmxbean Objectname: " + sb.toString());
		return sb.toString();
	}

	/**
	 * Extracts the package name from of given type.
	 * 
	 * @param className
	 *            the type name.
	 * @return the package name of the given type.
	 */
	private String getPackageName(String className) {
		String packageName = "";

		int plotIdx = className.lastIndexOf(".");
		if (plotIdx != -1) {
			packageName = className.substring(0, plotIdx);
		}
		return packageName;
	}

	public void stop() {
		if (m_objectName != null) {
			try {
				ManagementFactory.getPlatformMBeanServer().unregisterMBean(
						m_objectName);
			} catch (Exception e) {
				 logger.error("Unregistration of MBean failed.", e);
			}
			m_objectName = null;
		}
		m_MBean = null;
		instance = null;
		m_registered = false;
	}

	
	/* (non-Javadoc)
	 * @see com.dhgate.autojmx.AutoJMXHandler#getInstance()
	 */
	public ObjectInstance getInstance() {
		return instance;
	}

	
	/* (non-Javadoc)
	 * @see com.dhgate.autojmx.AutoJMXHandler#getUsedObjectName()
	 */
	public String getUsedObjectName() {
		if (m_objectName != null) {
			return m_objectName.toString();
		} else {
			return "";
		}
	}

	/* (non-Javadoc)
	 * @see com.dhgate.autojmx.AutoJMXHandler#isRegistered()
	 */
	public boolean isRegistered() {
		return m_registered;
	}

}
